using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
//using System.Security.Permissions;
using Microsoft.Win32;

namespace net.notdot.Attercop.Client
{
    public class PathNodeList : List<PathNode>
    {
        private PathNode m_Parent;
        public PathNode Parent
        {
            get
            {
                return m_Parent;
            }
        }

        public PathNodeList(PathNode parent)
        {
            m_Parent = parent;            
        }

        public new void Add(PathNode node)
        {
            if(node.Parent != null)
                throw new InvalidOperationException("Cannot add a PathNode to more than one PathNodeList");

            base.Add(node);
            node.m_Parent = this.Parent;
        }

        public new void AddRange(IEnumerable<PathNode> collection)
        {
            foreach (PathNode p in collection)
            {
                if (p.Parent != null)
                    throw new InvalidOperationException("Cannot add a PathNode to more than one PathNodeList");
                p.m_Parent = this.Parent;
            }

            base.AddRange(collection);
        }

        public new void Clear()
        {
            foreach(PathNode p in this)
            {
                p.m_Parent = null;
            }

            base.Clear();
        }

        public new void Insert(int index, PathNode node)
        {
            if (node.Parent != null)
                throw new InvalidOperationException("Cannot add a PathNode to more than one PathNodeList");
            node.m_Parent = this.Parent;

            base.Insert(index, node);
        }

        public new void InsertRange(int index, IEnumerable<PathNode> collection)
        {
            foreach (PathNode p in collection)
            {
                if (p.Parent != null)
                    throw new InvalidOperationException("Cannot add a PathNode to more than one PathNodeList");
                p.m_Parent = this.Parent;
            }

            base.InsertRange(index, collection);
        }

        public new bool Remove(PathNode node)
        {
            node.m_Parent = null;
            return base.Remove(node);
        }

        public new int RemoveAll(Predicate<PathNode> match)
        {
            foreach (PathNode p in this)
            {
                if (match(p))
                    p.m_Parent = null;
            }

            return base.RemoveAll(match);
        }

        public new void RemoveAt(int index)
        {
            this[index].m_Parent = null;
            base.RemoveAt(index);
        }

        public new void RemoveRange(int index, int count)
        {
            for(int i = 0; i < count; i++)
                this[i + index].m_Parent = null;
            base.RemoveRange(index, count);
        }
    }

    public class PathNode : IComparable<PathNode>
    {
        private static Dictionary<string, string> mimeMap = new Dictionary<string, string>();

        static PathNode()
        {
            //Populate the mimeMap
            //RegistryPermission regPerm = new RegistryPermission(RegistryPermissionAccess.Read, "\\\\HKEY_CLASSES_ROOT");
            RegistryKey typeKey = Registry.ClassesRoot.OpenSubKey("MIME\\Database\\Content Type");
            foreach (string keyName in typeKey.GetSubKeyNames())
            {
                RegistryKey curKey = typeKey.OpenSubKey(keyName);
                string key = (string)curKey.GetValue("Extension");
                if(key != null) {
                    key = key.ToLower();
                    if(!mimeMap.ContainsKey(key))
                        mimeMap.Add(key, keyName.ToLower());
                }
                curKey.Close();
            }
            typeKey.Close();
        }

        public static string GetMimeType(string extension)
        {
            string ret;
            if (mimeMap.TryGetValue(extension, out ret))
            {
                return ret;
            }
            else
            {
                return null;
            }
        }

        internal PathNode m_Parent = null;
        public PathNode Parent
        {
            get
            {
                return m_Parent;
            }
        }

        private PathNodeList m_Children;
        public PathNodeList Children
        {
            get
            {
                return m_Children;
            }
        }

        private string[] m_Terms;
        public string[] Terms
        {
            get
            {
                return m_Terms;
            }
        }

        private byte[] m_Hash;
        public byte[] Hash
        {
            get
            {
                return m_Hash;
            }
            set
            {
                m_Hash = value;
                if(HashCalculated != null)
                    HashCalculated(this, new EventArgs());
            }
        }

        public event EventHandler HashCalculated;

        private bool m_IsDir;
        public bool IsDir
        {
            get
            {
                return m_IsDir;
            }
        }

        private long m_TotalSize = 0;
        public long TotalSize
        {
            get
            {
                return m_TotalSize;
            }
            set
            {
                m_TotalSize = value;
            }
        }

        private string m_MimeType;
        public string MimeType
        {
            get
            {
                return m_MimeType;
            }
        }

        private string m_Extension;
        public string Extension
        {
            get
            {
                return m_Extension;
            }
        }

        public string TotalSizeHuman
        {
            get
            {
                return GetHumanSize((Double) m_TotalSize);
            }
        }

        static string[] multipliers = new string[] { "B", "KB", "MB", "GB", "TB", "EB" };
        public static string GetHumanSize(double size)
        {
            int multiplier = 0;

            while (size > 1000)
            {
                size /= 1024;
                multiplier++;
            }

            return string.Format("{0} {1}", size.ToString("#,##0.##"), multipliers[multiplier]);
        }

        public PathNode(string filename, bool isDir) {
            this.m_Name = filename;
            this.m_Terms = FileListingIndex.GetTerms(filename);
            this.m_IsDir = isDir;
            if(!isDir)
                this.m_MimeType = GetMimeType(Path.GetExtension(filename));
            this.m_Extension = Path.GetExtension(filename);
            m_Children = new PathNodeList(this);
        }

        public string FullName
        {
            get
            {
                if (Parent == null)
                {
                    return Name;
                }
                else
                {
                    return Path.Combine(((PathNode)Parent).FullName, Name);
                }
            }
        }

        private string m_Name;
        public string Name
        {
            get
            {
                return m_Name;
            }
        }

        #region IComparable<PathNode> Members

        public int CompareTo(PathNode other)
        {
            if (other == null) return 1;        //Everything is greater than null
            if (this.Equals(other)) return 0;   //If we're equal, we're equal

            int ret = this.IsDir.CompareTo(other.IsDir);
            if (ret != 0) return ret;           //If one is a dir and the other isn't, order by that

            ret = this.Name.CompareTo(other.Name);
            if (ret != 0) return ret;           //If our texts differ, order by that

            if (this.Parent != null)
            {
                return ((PathNode)this.Parent).CompareTo((PathNode) other.Parent);
            }
            else
            {
                if (other.Parent == null)
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
        }

        #endregion
    }
}
